package cn.cboard.gateway;

import java.util.ArrayList;

/**
 * @author
 *
 * 
 * A definition of a view that can be paginated
 */
public class PageDefn extends ViewDefn {

	public interface PageAction {
		public static final int FIRST = 0;
		public static final int NEXT = 1;
		public static final int PREVIOUS = 2;
		public static final int CURRENT = 3;
	}

	protected int pageSize;
	protected ColumnDesc[] rowIds;
	protected ColumnDesc[] allOrderBy;

	public PageDefn() {
		super();
		rowIds = null;
		pageSize = 50;
		allOrderBy = null;

	}

	public PageDefn(String viewname) {
		super(viewname);
		rowIds = null;
		pageSize = 50;
		allOrderBy = null;

	}

	/**
	 * Returns the pageSize.
	 * @return int
	 */
	public int getPageSize() {
		return pageSize;
	}

	/**
	 * Sets the pageSize.
	 * @param pageSize The pageSize to set
	 */
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
		super.setMaxRows(pageSize);
	}

	/**
	 * Return all columns that makeup a dicriminator
	 * this is an orderedlist of the order by columns and
	 * discriminator columns
	 */
	protected ColumnDesc[] getDisciminators() {
		ArrayList list = new ArrayList();
		if (orderBy != null) {
			for (int i = 0; i < orderBy.length; ++i) {
				list.add(orderBy[i]);
			}

		} // fi
		if (rowIds != null) {
			for (int i = 0; i < rowIds.length; ++i) {
				list.add(rowIds[i]);
			}

		} // fi
		return (ColumnDesc[]) list.toArray(new ColumnDesc[0]);
	}

	public ColumnDesc[] getOrderBy() {
		if (allOrderBy == null) {
			allOrderBy = getDisciminators();
		}
		return allOrderBy;
	}

	public String getWhereElement(int index, boolean reverse) {
		ColumnDesc[] orderBy = getOrderBy();
		StringBuffer element = new StringBuffer(" (");

		for (int i = 0; i < index; ++i) {
			element.append(orderBy[i].getColumn());
			element.append(" = ? ");
			element.append(" AND ");

		}

		element.append(orderBy[index].getAsWhere(reverse, false));
		element.append(" ) ");
		return element.toString();

	}

	public String getWhere(Object entry, boolean reverse) {
		ColumnDesc[] orderBy = getOrderBy();
		// use allOrderBy to formulate where
		StringBuffer where = new StringBuffer();
		for (int i = 0; i < orderBy.length; ++i) {
			String element = getWhereElement(i, reverse);
			where.append(element);
			if (i < (orderBy.length - 1)) {
				where.append(" OR ");
			}

		} //rof
		return where.toString();
	}

	

	public String getSql(PageContext context) {
		int action = context.getAction();
		ColumnDesc[] discs = getDisciminators();
		String sql = null;
		String where = new String();
		boolean reverse = false;
		switch (action) {
			case PageAction.FIRST :
				return super.getSQL(reverse);
			case PageAction.NEXT : // greater that last entry		
				where = getWhere(context.lastEntry, false);
				break;
			case PageAction.PREVIOUS :
				// less that first entry (using reverse ordering)                   
				where = getWhere(context.firstEntry, true);
				reverse = true;
				break;
		} // append to existing where clause if there is one
		String wh = super.getWhereClause();
		if (wh != null) {
			WhereDesc[] tmp = getWherePhrases();

			StringBuffer newWhere = new StringBuffer(" AND (");
			newWhere.append(where);
			newWhere.append(" ) ");

			WhereDesc discWhere = new WhereDesc(newWhere.toString());
			// Add Paginated where into base class
			addWherePhrase(discWhere);

			sql = super.getSql(reverse);

			// reset back to original
			setWherePhrases(tmp);
		} else {
			setWhereClause(where.toString());
			sql = super.getSql(reverse);
			setWhereClause(null);
		}

		return sql;
	}

	public Object[] getParams(PageContext context) {

		int action = context.getAction();
		Object[] first = (Object[]) context.firstEntry;
		Object[] last = (Object[]) context.lastEntry;
		Object[] disciminators = null;

		switch (context.getAction()) {
			case PageAction.FIRST :
				return super.getParams();
			case PageAction.PREVIOUS :
				disciminators = discWhere(first);
				return aggregate(getParams(), disciminators);
			case PageAction.NEXT :
				disciminators = discWhere(last);
				return aggregate(getParams(), disciminators);
		}
		return super.getParams();
	}

	public Object[] discWhere(Object[] entry) {
		ColumnDesc[] cols = getOrderBy();
		if (entry == null) {
			return new Object[0];
		}

		ArrayList disciminatorValues = new ArrayList();
		for (int i = 0; i < cols.length; ++i) {
			for (int j = 0; j <= i; ++j) {
				if (j < entry.length) {

					disciminatorValues.add(entry[j]);
				} else {
					disciminatorValues.add(null);
				}

			} //rof
		} //rof
		return disciminatorValues.toArray();
	}

	public static Object[] aggregate(Object[] a1, Object[] a2) {
		Object[] p = new Object[a1.length + a2.length];
		System.arraycopy(a1, 0, p, 0, a1.length);
		System.arraycopy(a2, 0, p, a1.length, a2.length);
		return p;
	}

	/**
	 * @return
	 */
	public ColumnDesc[] getRowIds() {
		return rowIds;
	}

	/**
	 * @param descs
	 */
	public void setRowIds(ColumnDesc[] descs) {
		rowIds = descs;
	}

}
